////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 2010.
// -------------------------------------------------------------------------
//  File name:   CryDLCStore.cpp
//  Version:     v1.00
//  Created:     02/09/2010 by Paul Mikell.
//  Description: CCryDLCStore member definitions
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////


#include "StdAfx.h"

#include "CryDLCStore.h"


#if USE_CRYLOBBY_GAMESPY


CCryDLCStore::CCryDLCStore( CCryLobby* pLobby, CCryLobbyService* pService )
:	CMultiThreadRefCount(),
	m_pLobby( pLobby ),
	m_pLobbyService( pService ),
	m_storeStatus( eCDLCSS_Unknown ),
	m_catalogRefreshing( false ),
	m_catalogReady( false ),
	m_catalogRefreshCount( 0 ),
	m_catalogUseCount( 0 ),
	m_catalogItems()
{
	for ( CryDLCStoreTaskID dlcsTaskID = 0; dlcsTaskID < MAX_DLCSTORE_TASKS; ++dlcsTaskID )
	{
		m_pTasks[ dlcsTaskID ] = NULL;
	}

	for ( CryDLCOrderHandle h = 0; h < MAX_DLCSTORE_ORDERS; ++h )
	{
		m_pOrders[ h ] = NULL;
	}
}


ECryLobbyError CCryDLCStore::Initialise()
{
	for ( CryDLCStoreTaskID dlcsTaskID = 0; dlcsTaskID < MAX_DLCSTORE_TASKS; ++dlcsTaskID )
	{
		ClearTask( dlcsTaskID );
	}

	for ( CryDLCOrderHandle orderHandle = 0; orderHandle < MAX_DLCSTORE_ORDERS; ++orderHandle )
	{
		ClearOrderHandle( orderHandle );
	}

	return eCLE_Success;
}


ECryLobbyError CCryDLCStore::Terminate()
{
	return eCLE_Success;
}


void CCryDLCStore::Tick( CTimeValue tv )
{
}


ECryLobbyError CCryDLCStore::StartTaskWithNewOrder( uint32 eTask, CryDLCStoreTaskID* pDLCSTaskID, CryLobbyTaskID* pLTaskID, CryUserID userID, CryDLCOrderHandle* pOrderHandle, void* pCb, void* pCbArg )
{
	ECryLobbyError		result;

	result = CreateOrderHandle( userID, pOrderHandle );

	if ( result == eCLE_Success )
	{
		result = StartTaskWithOrder( eTask, pDLCSTaskID, pLTaskID, *pOrderHandle, pCb, pCbArg );

		if ( result != eCLE_Success )
		{
			FreeOrderHandle( *pOrderHandle );
		}
	}

	return result;
}


ECryLobbyError CCryDLCStore::StartTaskWithExistingOrder( uint32 eTask, CryDLCStoreTaskID* pDLCSTaskID, CryLobbyTaskID* pLTaskID, CryDLCOrderHandle orderHandle, void* pCb, void* pCbArg )
{
	ECryLobbyError		result = eCLE_Success;
	SOrder*						pOrder;

	if ( orderHandle < MAX_DLCSTORE_ORDERS )
	{
		pOrder = m_pOrders[ orderHandle ];

		if ( !pOrder->used )
		{
			result = eCLE_InvalidOrder;
		}
		else
		{
			if ( pOrder->task != CryDLCStoreInvalidTaskID )
			{
				result = eCLE_OrderInUse;
			}
		}
	}
	else
	{
		result = eCLE_InvalidOrder;
	}

	if ( result == eCLE_Success )
	{
		result = StartTaskWithOrder( eTask, pDLCSTaskID, pLTaskID, orderHandle, pCb, pCbArg );
	}

	return result;
}


ECryLobbyError CCryDLCStore::StartTaskWithNoOrder( uint32 eTask, CryDLCStoreTaskID* pDLCSTaskID, CryLobbyTaskID* pLTaskID, void* pCb, void* pCbArg )
{
	return StartTask( eTask, pDLCSTaskID, pLTaskID, CryDLCOrderInvalidHandle, pCb, pCbArg );
}


ECryLobbyError CCryDLCStore::StartTaskWithOrder( uint32 eTask, CryDLCStoreTaskID* pDLCSTaskID, CryLobbyTaskID* pLTaskID, CryDLCOrderHandle orderHandle, void* pCb, void* pCbArg )
{
	ECryLobbyError		result;
	CryDLCStoreTaskID dlcsTaskID;

	if ( !pDLCSTaskID )
	{
		pDLCSTaskID = &dlcsTaskID;
	}

	result = StartTask( eTask, pDLCSTaskID, pLTaskID, orderHandle, pCb, pCbArg );

	if ( result == eCLE_Success )
	{
		m_pOrders[ orderHandle ]->task = *pDLCSTaskID;
	}

	return result;
}


ECryLobbyError CCryDLCStore::StartTask( uint32 eTask, CryDLCStoreTaskID* pDLCSTaskID, CryLobbyTaskID* pLTaskID, CryDLCOrderHandle orderHandle, void* pCb, void* pCbArg )
{
	ECryLobbyError		result = eCLE_TooManyTasks;
	STask*						pTask = NULL;
	CryDLCStoreTaskID dlcsTaskID;
	CryLobbyTaskID		lTaskID;

	if ( !pDLCSTaskID )
	{
		pDLCSTaskID = &dlcsTaskID;
	}

	if ( !pLTaskID )
	{
		pLTaskID = &lTaskID;
	}

	for ( *pDLCSTaskID = 0; *pDLCSTaskID < MAX_DLCSTORE_TASKS; ++*pDLCSTaskID )
	{
		pTask = m_pTasks[ *pDLCSTaskID ];

		CRY_ASSERT_MESSAGE( pTask, "CCryDLCStore: Task base pointers not setup" );

		if ( !pTask->used )
		{
			break;
		}
	}

	if ( *pDLCSTaskID < MAX_DLCSTORE_TASKS )
	{
		*pLTaskID = m_pLobby->CreateTask();

		if ( *pLTaskID != CryLobbyInvalidTaskID )
		{
			CreateTask( pTask, eTask, *pLTaskID, orderHandle, pCb, pCbArg );
			result = eCLE_Success;
		}
	}

	return result;
}


void CCryDLCStore::CreateTask( STask* pTask, uint32 eTask, CryLobbyTaskID lTaskID, CryDLCOrderHandle orderHandle, void* pCb, void* pCbArg )
{
	pTask->lTaskID = lTaskID;
	pTask->error = eCLE_Success;
	pTask->startedTask = eTask;
	pTask->subTask = eTask;
	pTask->order = orderHandle;
	pTask->pCb = pCb;
	pTask->pCbArg = pCbArg;
	pTask->used = true;
	pTask->running = false;
	pTask->cancelled = false;
	pTask->refreshingCatalog = false;
	pTask->usingCatalog = false;

	for ( uint32 j = 0; j < MAX_DLCSTORE_PARAMS; ++j )
	{
		pTask->paramsMem[ j ] = TMemInvalidHdl;
		pTask->paramsNum[ j ] = 0;
	}
}


void CCryDLCStore::StartSubTask( uint32 etask, CryDLCStoreTaskID dlcsTaskID )
{
	STask* pTask = m_pTasks[ dlcsTaskID ];

	pTask->subTask = etask;
}


void CCryDLCStore::StartSubTask( uint32 etask, STask* pTask )
{
	pTask->subTask = etask;
}


void CCryDLCStore::FreeTask( CryDLCStoreTaskID dlcsTaskID )
{
	STask* pTask = m_pTasks[ dlcsTaskID ];

	assert( pTask );

	for ( uint32 i = 0; i < MAX_DLCSTORE_PARAMS; ++i )
	{
		if ( pTask->paramsMem[ i ] != TMemInvalidHdl )
		{
			m_pLobby->MemFree( pTask->paramsMem[ i ] );
		}
	}

	if ( pTask->lTaskID != CryLobbyInvalidTaskID )
	{
		m_pLobby->ReleaseTask( pTask->lTaskID );
	}

	if ( pTask->order != CryDLCOrderInvalidHandle )
	{
		m_pOrders[ pTask->order ]->task = CryDLCStoreInvalidTaskID;
	}

	ClearTask( dlcsTaskID );
}


void CCryDLCStore::ClearTask( CryDLCStoreTaskID dlcsTaskID )
{
	STask* pTask = m_pTasks[ dlcsTaskID ];

	assert( pTask );

	pTask->lTaskID = CryLobbyInvalidTaskID;
	pTask->error = eCLE_Success;
	pTask->order = CryDLCOrderInvalidHandle;
	pTask->pCb = NULL;
	pTask->pCbArg = NULL;

	for ( uint32 i = 0; i < MAX_DLCSTORE_PARAMS; ++i )
	{
		pTask->paramsMem[ i ] = TMemInvalidHdl;
		pTask->paramsNum[ i ] = 0;
	}

	pTask->used = false;
	pTask->running = false;
	pTask->cancelled = false;
	pTask->refreshingCatalog = false;
	pTask->usingCatalog = false;
}


void CCryDLCStore::CancelTask( CryLobbyTaskID lTaskID )
{
	LOBBY_AUTO_LOCK;

	NetLog( "[Lobby]Try cancel task %d", lTaskID );

	if ( lTaskID != CryLobbyInvalidTaskID )
	{
		for ( CryDLCStoreTaskID dlcsTaskID = 0; dlcsTaskID < MAX_DLCSTORE_TASKS; ++dlcsTaskID )
		{
			STask*	pTask = m_pTasks[ dlcsTaskID ];
			
			assert( pTask );

			if ( pTask->used && ( pTask->lTaskID == lTaskID ) )
			{
				NetLog( "[Lobby] Task %d cancelled", lTaskID );
				pTask->pCb = NULL;
				pTask->cancelled = true;

				break;
			}
		}
	}
}


ECryLobbyError CCryDLCStore::CreateTaskParamMem( CryDLCStoreTaskID dlcsTaskID, uint32 param, const void* pParamData, size_t paramDataSize )
{
	return CreateTaskParamMem( m_pTasks[ dlcsTaskID ], param, pParamData, paramDataSize );
}


ECryLobbyError CCryDLCStore::CreateTaskParamMem( STask* pTask, uint32 param, const void* pParamData, size_t paramDataSize )
{
	ECryLobbyError	result = eCLE_Success;

	CRY_ASSERT_MESSAGE( pTask, "CCryStats: Task base pointers not setup" );

	if ( paramDataSize > 0 )
	{
		pTask->paramsMem[ param ] = m_pLobby->MemAlloc( paramDataSize );
		void* p = m_pLobby->MemGetPtr( pTask->paramsMem[ param ] );

		if ( p )
		{
			if ( pParamData )
			{
				memcpy( p, pParamData, paramDataSize );
			}
		}
		else
		{
			result = eCLE_OutOfMemory;
		}
	}

	return result;
}


void CCryDLCStore::UpdateTaskError( CryDLCStoreTaskID dlcsTaskID, ECryLobbyError error )
{
	UpdateTaskError( m_pTasks[ dlcsTaskID ], error );
}


void CCryDLCStore::UpdateTaskError( STask* pTask, ECryLobbyError error )
{
	CRY_ASSERT_MESSAGE( pTask, "CCryDLCStore: Task base pointers not setup" );

	if ( pTask->error == eCLE_Success )
	{
		pTask->error = error;
	}
}


void CCryDLCStore::StartTaskRunning( CryDLCStoreTaskID dlcsTaskID )
{
	LOBBY_AUTO_LOCK;

	STask* pTask = m_pTasks[ dlcsTaskID ];

	if ( pTask->used )
	{
		pTask->running = true;
	}
}


void CCryDLCStore::StopTaskRunning( CryDLCStoreTaskID dlcsTaskID )
{
	STask* pTask = m_pTasks[ dlcsTaskID ];

	if ( pTask->used )
	{
		pTask->running = false;

		TO_GAME_FROM_LOBBY( &CCryDLCStore::EndTask, this, dlcsTaskID );
	}
}


void CCryDLCStore::EndTask( CryDLCStoreTaskID dlcsTaskID )
{
	LOBBY_AUTO_LOCK;

	StopTaskRefreshingCatalog( dlcsTaskID );
	StopTaskUsingCatalog( dlcsTaskID );
	FreeTask( dlcsTaskID );
}


void CCryDLCStore::SetDownloadableVerifyCallback( CryDLCDownloadableVerifyCallback cb )
{
	m_downloadableVerifyCallback = cb;
}


bool CCryDLCStore::IsDownloading()
{
	LOBBY_AUTO_LOCK;

	bool		result = false;

	for ( CryDLCStoreTaskID dlcsTaskID = 0; ( dlcsTaskID < MAX_DLCSTORE_TASKS ) && !result; ++dlcsTaskID )
	{
		STask*	pTask = m_pTasks[ dlcsTaskID ];

		if ( ( pTask->used ) && ( ( pTask->startedTask == eT_UserDownloadItem ) || ( pTask->startedTask == eT_UserRedownloadAllMissingItems ) ) )
		{
			result = true;
		}
	}

	return result;
}


bool CCryDLCStore::StartTaskRefreshingCatalog( CryDLCStoreTaskID dlcsTaskID )
{
	bool		result = false;
	STask*	pTask = m_pTasks[ dlcsTaskID ];

	if ( pTask->used && ( m_catalogUseCount == 0 ) && !pTask->refreshingCatalog )
	{
		pTask->refreshingCatalog = true;
		++m_catalogRefreshCount;
		result = true;
	}

	return result;
}


bool CCryDLCStore::StopTaskRefreshingCatalog( CryDLCStoreTaskID dlcsTaskID )
{
	bool		result = false;
	STask*	pTask = m_pTasks[ dlcsTaskID ];

	if ( pTask->used && pTask->refreshingCatalog )
	{
		pTask->refreshingCatalog = false;
		--m_catalogRefreshCount;
		result = true;
	}

	return result;
}


bool CCryDLCStore::StartTaskUsingCatalog( CryDLCStoreTaskID dlcsTaskID )
{
	bool		result = false;
	STask*	pTask = m_pTasks[ dlcsTaskID ];

	if ( pTask->used && ( m_catalogRefreshCount == 0 ) && !m_catalogRefreshing && m_catalogReady && !pTask->usingCatalog )
	{
		pTask->usingCatalog = true;
		++m_catalogUseCount;
		result = true;
	}

	return result;
}


bool CCryDLCStore::StopTaskUsingCatalog( CryDLCStoreTaskID dlcsTaskID )
{
	bool		result = false;
	STask*	pTask = m_pTasks[ dlcsTaskID ];

	if ( pTask->used && pTask->usingCatalog )
	{
		pTask->usingCatalog = false;
		--m_catalogUseCount;
		result = true;
	}

	return result;
}


ECryLobbyError CCryDLCStore::CreateOrderHandle( CryUserID userID, CryDLCOrderHandle* pOrderHandle )
{
	ECryLobbyError		error;
	CryDLCOrderHandle	orderHandle;

	for ( orderHandle = 0; orderHandle < MAX_DLCSTORE_ORDERS; ++orderHandle )
	{
		SOrder*						pOrder = m_pOrders[ orderHandle ];

		if ( !pOrder->used )
		{
			ClearOrderHandle( orderHandle );
			pOrder->used = true;
			pOrder->userID = userID;
			*pOrderHandle = orderHandle;
			error = eCLE_Success;
			break;
		}
	}

	if ( orderHandle == MAX_DLCSTORE_ORDERS )
	{
		*pOrderHandle = CryDLCOrderInvalidHandle;
		error = eCLE_TooManyOrders;
	}

	return error;
}


void CCryDLCStore::FreeOrderHandle( CryDLCOrderHandle orderHandle )
{
	ClearOrderHandle( orderHandle );
}


void CCryDLCStore::ClearOrderHandle( CryDLCOrderHandle orderHandle )
{
	SOrder*						pOrder = m_pOrders[ orderHandle ];

	pOrder->userID = NULL;
	pOrder->paymentMethodID = NULL;
	pOrder->itemQuantities.clear();
	pOrder->transactionState = eTS_NotStarted;
	pOrder->task = CryDLCStoreInvalidTaskID;
	pOrder->used = false;
}


void CCryDLCStore::UpdateDownloadable( SCryDLCDownloadableInfo& info )
{
	std::vector< SCryDLCDownloadableInfo >::iterator it;

	for ( it = m_downloadables.begin(); it < m_downloadables.end(); ++it )
	{
		if ( *( it->id ) == *( info.id ) )
		{
			break;
		}
	}

	if ( it == m_downloadables.end() )
	{
		m_downloadables.push_back( info );
	}
	else
	{
		it->name = info.name;
		it->assetType = info.assetType;
		it->sequence = info.sequence;
		it->version = info.version;
	}
}


#endif // USE_CRYLOBBY_GAMESPY
